package cn.iocoder.yudao.module.product.spu.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.thirdparty.zxhx.dto.ZXHXSpuDetailsRespDTO;
import cn.iocoder.yudao.framework.util.TransactionUtils;
import cn.iocoder.yudao.module.api.infrastructure.config.ConfigApiV2;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.MallConfigRespDTO;
import cn.iocoder.yudao.module.api.product.sku.dto.ProductPropertyValueRespDTO;
import cn.iocoder.yudao.module.api.product.spu.enums.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.api.shop.category.ShopCategoryApi;
import cn.iocoder.yudao.module.api.shop.category.eums.ShopCategoryCodeEnum;
import cn.iocoder.yudao.module.api.supply.enums.SupplySpuStatusEnum;
import cn.iocoder.yudao.module.api.trade.delivery.enums.DeliveryTypeEnum;
import cn.iocoder.yudao.module.product.brand.dal.dataobject.ProductBrandDO;
import cn.iocoder.yudao.module.product.brand.dal.mysql.ProductBrandMapper;
import cn.iocoder.yudao.module.product.category.dal.dataobject.ProductCategoryDO;
import cn.iocoder.yudao.module.product.category.dal.mysql.ProductCategoryMapper;
import cn.iocoder.yudao.module.product.property.dal.dataobject.ProductPropertyDO;
import cn.iocoder.yudao.module.product.property.dal.dataobject.ProductPropertyValueDO;
import cn.iocoder.yudao.module.product.property.dal.mysql.ProductPropertyMapper;
import cn.iocoder.yudao.module.product.property.dal.mysql.ProductPropertyValueMapper;
import cn.iocoder.yudao.module.product.property.service.ProductPropertyService;
import cn.iocoder.yudao.module.product.selection.pojo.vo.ProductSupplySelectionQueryDTO;
import cn.iocoder.yudao.module.product.selection.pojo.vo.ProductSupplySelectionVO;
import cn.iocoder.yudao.module.product.selection.service.ProductSupplySelectionService;
import cn.iocoder.yudao.module.product.sku.dal.dataobject.ProductSkuDO;
import cn.iocoder.yudao.module.product.sku.dal.mysql.ProductSkuMapper;
import cn.iocoder.yudao.module.product.spu.dal.dataobject.ProductSpuDO;
import cn.iocoder.yudao.module.product.spu.dal.mysql.ProductSpuMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMinValue;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_MALL;
import static cn.iocoder.yudao.module.api.product.ConfigConstants.PROPERTY_NAME_DEFAULT;
import static cn.iocoder.yudao.module.api.product.ConfigConstants.PROPERTY_VALUE_NAME_DEFAULT;
import static cn.iocoder.yudao.module.api.shop.ConfigConstants.MALL_PLATFORM_CATEGORY_ID;
import static cn.iocoder.yudao.module.api.shop.ConfigConstants.SELF_OPERATED_SHOP_ID;
import static cn.iocoder.yudao.module.api.supply.ConfigConstants.SUPPLY_NAME_ZXHX;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  这里是文件说明
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/03/04
 */
@Service
@Validated
@Slf4j
public class ProductFromSelectionSyncServiceImpl implements ProductFromSelectionSyncService {
    @Resource
    private ShopCategoryApi shopCategoryApi;
    @Resource
    private ConfigApiV2 configApiV2;

    @Resource
    private ProductBrandMapper productBrandMapper;
    @Resource
    private ProductCategoryMapper productCategoryMapper;
    @Resource
    private ProductSpuMapper productSpuMapper;
    @Resource
    private ProductSkuMapper productSkuMapper;
    @Resource
    private ProductPropertyMapper productPropertyMapper;
    @Resource
    private ProductPropertyValueMapper productPropertyValueMapper;

    @Resource
    private ProductSupplySelectionService productSupplySelectionService;
    @Resource
    private ProductPropertyService productPropertyService;

    private MallConfigRespDTO mallConfigRespDTO;
    private final Lock lock = new ReentrantLock();
    private int savedCount = 0;

    @PostConstruct
    private void init() {
        mallConfigRespDTO = configApiV2.getConfig(CONFIG_MALL, MallConfigRespDTO.class);
    }

    @Override
    public int autoSync(int pageNo, int pageSize) throws Exception {
        pageNo = Math.max(1, pageNo);
        ProductSupplySelectionQueryDTO queryVO = new ProductSupplySelectionQueryDTO();
        queryVO.setIncludeSnapshot(true);
        PageResult<ProductSupplySelectionVO> pageResult = productSupplySelectionService.getPage(pageNo, pageSize, queryVO);
        List<ProductSupplySelectionVO> respVOList = pageResult.getList();
        if (CollUtil.isEmpty(respVOList)) {
            log.info("自营店第 {} 页没有商品，每页 {} 个商品", pageNo, pageSize);
            return savedCount;
        }
        log.info("自营店共有 {} 个商品，第 {} 页共获取到 {} 个商品", pageResult.getTotal(), pageNo, respVOList.size());
        List<Long> removedRelatedIds = new ArrayList<>();
        List<Long> ignoreRelatedIds = new ArrayList<>();
        for (ProductSupplySelectionVO respVO : respVOList) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                log.warn("自营店同步商品时 sleep error", e);
            }
            if (SupplySpuStatusEnum.REMOVED.getValue().equals(respVO.getStatus())) {
                log.info("自营店移除商品SpuID:{}，商品名称:{}", respVO.getSpuId(), respVO.getSpuName());
                removedRelatedIds.add(respVO.getSpuId());
                continue;
            }
            ZXHXSpuDetailsRespDTO spuDetailsRespVO = respVO.getSpuDetails();
            if (spuDetailsRespVO == null) {
                log.warn("自营店忽略快照数据缺失商品SpuID:{}，商品名称:{}", respVO.getSpuId(), respVO.getSpuName());
                ignoreRelatedIds.add(respVO.getSpuId());
                continue;
            }
            if (spuDetailsRespVO.getIsOverseas() == 1) {
                log.warn("自营店忽略海外商品SpuID:{}，商品名称:{}", respVO.getSpuId(), respVO.getSpuName());
                ignoreRelatedIds.add(respVO.getSpuId());
                continue;
            }
            // 此处后续可以使用 JDK 21 的虚拟线程代替
            Executors.newCachedThreadPool().submit(() -> {
                try {
                    if (CollUtil.isEmpty(spuDetailsRespVO.getSku())) {
                        spuDetailsRespVO.setSku(new ArrayList<>());
                        spuDetailsRespVO.setSpecType(0);
                    }
                    TransactionUtils.transactional(() -> {
                        syncSpu(spuDetailsRespVO, respVO.getUpdateTime());
                        return null;
                    });
                } catch (Exception e) {
                    log.error("自营店同步商品失败，SpuID:{}，商品名称:{}，原因:{}", spuDetailsRespVO.getSpuId(), spuDetailsRespVO.getStoreName(), e.getMessage());
                }
            });
        }
        log.info("自营店第 {} 页共忽略 {} 个商品，移除 {} 个商品", pageNo, ignoreRelatedIds.size(), removedRelatedIds.size());
        if (CollUtil.isNotEmpty(removedRelatedIds)) {
            List<Long> productSpuIds = productSpuMapper.selectSpuIdsBySupplyRelatedIds(removedRelatedIds);
            List<ProductSkuDO> productSkuDOS = productSkuMapper.selectListBySpuIds(productSpuIds);
            TransactionUtils.transactional(() -> {
                productSpuMapper.deleteByIds(productSpuIds);
                productSkuMapper.deleteByIds(productSkuDOS.stream().map(ProductSkuDO::getId).collect(Collectors.toList()));
                return null;
            });
        }
        Thread.sleep(1000);
        return autoSync(pageNo + 1, pageSize);
    }

    private void syncSpu(ZXHXSpuDetailsRespDTO spu, LocalDateTime updateTime) {
        ProductSpuDO spuDO = buildSpu(spu);
        ProductSpuDO spuExist = productSpuMapper.selectBySupplyRelatedId(spu.getSpuId());
        if (spuExist != null) {
            LocalDateTime spuExistUpdateTime = spuExist.getUpdateTime();
            if (spuExistUpdateTime != null && (spuExistUpdateTime.isEqual(updateTime) || spuExistUpdateTime.isAfter(updateTime))) {
                log.debug("自营店商品SpuID:{}，商品名称:{}，供应链选品库更新时间为:{}，无需更新", spu.getSpuId(), spu.getStoreName(), updateTime);
                return;
            }
            log.debug("自营店商品SpuID:{}，商品名称:{}，更新时间为：{}，供应链选品库更新时间为:{}，需更新", spu.getSpuId(), spu.getStoreName(), spuDO.getUpdateTime(), updateTime);
            spuDO.setId(spuExist.getId());
            spuDO.setUpdateTime(updateTime);
            productSpuMapper.updateById(spuDO);
            syncSku(spuDO.getId(), spu.getSku());
            return;
        }
        if (spu.getStatus() == 1) {
            log.warn("自营店忽略下架商品SpuID:{}，商品名称:{}", spu.getSpuId(), spu.getStoreName());
            return;
        }
        productSpuMapper.insert(spuDO);
        syncSku(spuDO.getId(), spu.getSku());
        try {
            lock.lock();
            savedCount++;
        } finally {
            lock.unlock();
        }
    }

    private ProductSpuDO buildSpu(ZXHXSpuDetailsRespDTO spu) {
        // 第三方供应链的成本价
        int costPrice = spu.getPrice().intValue() * 100;
        // 第三方供应链的市场价
        int marketPrice = spu.getMarketPrice().intValue() * 100;
        // 第三方供应链的折扣
        int discount = (int) (NumberUtil.div(costPrice, marketPrice) * 10);
        log.info("自营店商品SpuID:{}，商品名称:{}，成本成本价:{}，建议零售价:{}，折扣:{}", spu.getSpuId(), spu.getStoreName(), spu.getPrice(), spu.getMarketPrice(), discount);
        // 平台成本价定价规则：在供应链成本成本价的基础上加价
        costPrice = (int) (costPrice + (costPrice * mallConfigRespDTO.getSupplyCostPricePercent() / 100f));
        // 平台批发价定价规则：在供应链建议零售价的基础上减价，且不低于成本价
        int wholesalePrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplyWholesalePricePercent() / 100f));
        wholesalePrice = Math.max(costPrice, wholesalePrice);
        // 平台会员价定价规则：在供应链建议零售价的基础上减价，且不低于批发价
        int vipPrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplyVipPricePercent() / 100f));
        vipPrice = Math.max(wholesalePrice, vipPrice);
        // 平台零售价定价规则：在供应链建议零售价的基础上减价，且不低于会员价
        int salePrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplySalePricePercent() / 100f));
        salePrice = Math.max(vipPrice, salePrice);
        long supplySpuClassId = NumberUtil.parseLong(spu.getClassId());
        long platformCategoryId = shopCategoryApi.getIdBySupplyRelatedId(supplySpuClassId);
        ProductCategoryDO categoryDO = productCategoryMapper.selectBySupplyRelatedId(supplySpuClassId);
        ProductBrandDO brandDO = productBrandMapper.selectBySupplyRelatedId(spu.getBrandId());
        ProductSpuDO productSpuDO = new ProductSpuDO();
        productSpuDO.setType(ShopCategoryCodeEnum.MALL.getCode());
        productSpuDO.setShopId(SELF_OPERATED_SHOP_ID);
        productSpuDO.setName(spu.getStoreName());
        productSpuDO.setKeyword(spu.getKeyword());
        productSpuDO.setIntroduction(spu.getStoreInfo());
        productSpuDO.setDescription(spu.getDescription());
        productSpuDO.setBarCode(spu.getCode());
        productSpuDO.setPlatformCategoryId(platformCategoryId == 0L ? MALL_PLATFORM_CATEGORY_ID : platformCategoryId);
        productSpuDO.setCategoryId(Optional.ofNullable(categoryDO).map(ProductCategoryDO::getId).orElse(0L));
        productSpuDO.setBrandId(Optional.ofNullable(brandDO).map(ProductBrandDO::getId).orElse(0L));
        productSpuDO.setPicUrl(spu.getImage());
        productSpuDO.setSliderPicUrls(spu.getSliderImage());
        productSpuDO.setUnitName(spu.getUnitName());
        productSpuDO.setSpecType(spu.getSpecType() == 1);
        productSpuDO.setDeliveryTypes(Collections.singletonList(DeliveryTypeEnum.EXPRESS.getType()));
        productSpuDO.setStock(getSumValue(spu.getSku(), ZXHXSpuDetailsRespDTO.SkuDTO::getStock, Integer::sum));
        productSpuDO.setPrice(salePrice);
        productSpuDO.setVipPrice(vipPrice);
        productSpuDO.setMarketPrice(marketPrice);
        productSpuDO.setCostPrice(costPrice);
        productSpuDO.setWholesaleForRetailPrice(wholesalePrice);
        productSpuDO.setIsIntegral(discount > 5);
        productSpuDO.setLimitMinQuantity(getMinValue(spu.getSku(), ZXHXSpuDetailsRespDTO.SkuDTO::getBuyStartQty));
        productSpuDO.setGuidance(spu.getInfo());
        productSpuDO.setStatus(ProductSpuStatusEnum.ENABLE.getStatus());
        productSpuDO.setSupplyName(SUPPLY_NAME_ZXHX);
        productSpuDO.setSupplyRelatedId(spu.getSpuId());
        return productSpuDO;
    }

    private void syncSku(Long spuId, List<ZXHXSpuDetailsRespDTO.SkuDTO> skuDTOList) {
        if (CollUtil.isEmpty(skuDTOList)) {
            return;
        }
        List<ProductSkuDO> skuDOS = new ArrayList<>();
        for (ZXHXSpuDetailsRespDTO.SkuDTO skuDTO : skuDTOList) {
            List<ProductPropertyValueRespDTO> properties = new ArrayList<>();
            if (CollUtil.isEmpty(skuDTO.getAttr())) {
                ZXHXSpuDetailsRespDTO.SkuDTO.AttrDTO attrDTO = new ZXHXSpuDetailsRespDTO.SkuDTO.AttrDTO();
                attrDTO.setName(PROPERTY_NAME_DEFAULT);
                attrDTO.setValue(PROPERTY_VALUE_NAME_DEFAULT);
                skuDTO.setAttr(List.of(attrDTO));
            }
            for (ZXHXSpuDetailsRespDTO.SkuDTO.AttrDTO attr : skuDTO.getAttr()) {
                ProductPropertyValueRespDTO respDTO = new ProductPropertyValueRespDTO();
                respDTO.setPropertyName(attr.getName());
                respDTO.setValueName(attr.getValue());
                properties.add(respDTO);
            }
            skuDOS.add(buildSku(spuId, skuDTO, properties));
        }
        Set<Long> relatedSkuIds = skuDTOList.stream().map(ZXHXSpuDetailsRespDTO.SkuDTO::getSkuId).collect(Collectors.toSet());
        List<ProductSkuDO> skuExist = productSkuMapper.selectBySupplyRelatedIds(relatedSkuIds);
        List<ProductSkuDO> insertSkuDOS = new ArrayList<>();
        List<ProductSkuDO> updateSkuDOS = new ArrayList<>();
        for (ProductSkuDO skuDO : skuDOS) {
            boolean exist = false;
            for (ProductSkuDO sku : skuExist) {
                if (Objects.equals(sku.getSupplyRelatedId(), skuDO.getSupplyRelatedId())) {
                    skuDO.setId(sku.getId());
                    exist = true;
                    break;
                }
            }
            if (exist) {
                updateSkuDOS.add(skuDO);
            } else {
                insertSkuDOS.add(skuDO);
            }
        }
        skuDOS.clear();
        productSkuMapper.insertBatch(insertSkuDOS);
        productSkuMapper.updateById(updateSkuDOS);
        log.info("自营店更新商品成功，SpuID:{}", spuId);
        TransactionUtils.afterCommit(() -> {
            List<ProductSkuDO> skuDOList = new ArrayList<>();
            skuDOList.addAll(insertSkuDOS);
            skuDOList.addAll(updateSkuDOS);
            syncProperty(skuDOList);
            return null;
        });
    }

    private ProductSkuDO buildSku(Long spuId, ZXHXSpuDetailsRespDTO.SkuDTO skuDTO, List<ProductPropertyValueRespDTO> properties) {
        // 第三方供应链的成本价
        int costPrice = skuDTO.getPrice().intValue() * 100;
        // 第三方供应链的市场价
        int marketPrice = skuDTO.getMarketPrice().intValue() * 100;
        // 第三方供应链的折扣
        int discount = (int) (NumberUtil.div(costPrice, marketPrice) * 10);
        log.info("自营店商品SkuID:{}，规格名称:{}，成本成本价:{}，建议零售价:{}，服务费:{}，折扣:{}", skuDTO.getSkuId(), skuDTO.getSuk(), skuDTO.getPrice(), skuDTO.getMarketPrice(), skuDTO.getServicePrice(), discount);
        // 平台成本价定价规则：在供应链成本成本价的基础上加价
        costPrice = (int) (costPrice + (costPrice * mallConfigRespDTO.getSupplyCostPricePercent() / 100f));
        // 平台批发价定价规则：在供应链建议零售价的基础上减价，且不低于成本价
        int wholesalePrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplyWholesalePricePercent() / 100f));
        wholesalePrice = Math.max(costPrice, wholesalePrice);
        // 平台会员价定价规则：在供应链建议零售价的基础上减价，且不低于批发价
        int vipPrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplyVipPricePercent() / 100f));
        vipPrice = Math.max(wholesalePrice, vipPrice);
        // 平台零售价定价规则：在供应链建议零售价的基础上减价，且不低于会员价
        int salePrice = (int) (marketPrice - (marketPrice * mallConfigRespDTO.getSupplySalePricePercent() / 100f));
        salePrice = Math.max(vipPrice, salePrice);
        ProductSkuDO productSkuDO = new ProductSkuDO();
        productSkuDO.setShopId(SELF_OPERATED_SHOP_ID);
        productSkuDO.setSpuId(spuId);
        productSkuDO.setPicUrl(skuDTO.getImage());
        productSkuDO.setPrice(salePrice);
        productSkuDO.setVipPrice(vipPrice);
        productSkuDO.setMarketPrice(marketPrice);
        productSkuDO.setCostPrice(costPrice);
        productSkuDO.setWholesaleForRetailPrice(wholesalePrice);
        productSkuDO.setStock(skuDTO.getStock());
        productSkuDO.setVolume(skuDTO.getVolume());
        productSkuDO.setWeight(skuDTO.getWeight());
        productSkuDO.setBarCode(skuDTO.getBarCode());
        productSkuDO.setProperties(properties);
        productSkuDO.setSupplyName(SUPPLY_NAME_ZXHX);
        productSkuDO.setSupplyRelatedId(skuDTO.getSkuId());
        return productSkuDO;
    }

    private void syncProperty(List<ProductSkuDO> skuDOS) {
        Map<String, Set<String>> batchPropertyMap = new TreeMap<>();
        for (ProductSkuDO skuDO : skuDOS) {
            List<ProductPropertyValueRespDTO> skuProperties = skuDO.getProperties();
            if (CollUtil.isEmpty(skuProperties)) {
                continue;
            }
            for (ProductPropertyValueRespDTO property : skuProperties) {
                batchPropertyMap.computeIfAbsent(property.getPropertyName(), key -> new HashSet<>()).add(property.getValueName());
            }
        }
        Long shopId = skuDOS.get(0).getShopId();
        productPropertyService.trySaveProperties(shopId, batchPropertyMap, SUPPLY_NAME_ZXHX);
        List<ProductPropertyDO> propertyExists = productPropertyMapper.selectListByShopId(shopId);
        Set<Long> propertyIds = propertyExists.stream().map(ProductPropertyDO::getId).collect(Collectors.toSet());
        List<ProductPropertyValueDO> propertyValueExists = productPropertyValueMapper.selectListByPropertyIds(propertyIds);
        log.debug("[syncProperty][skuDOS({})个获得 propertyExists({})，propertyValueExists({})]", skuDOS.size(), propertyExists.stream().map(ProductPropertyDO::getName).toList(), propertyValueExists.stream().map(ProductPropertyValueDO::getName).toList());
        List<ProductSkuDO> updateObjs = new ArrayList<>();
        for (ProductSkuDO skuDO : skuDOS) {
            List<ProductPropertyValueRespDTO> skuProperties = skuDO.getProperties();
            if (CollUtil.isEmpty(skuProperties)) {
                continue;
            }
            ProductSkuDO updateObj = new ProductSkuDO();
            updateObj.setId(skuDO.getId());
            for (ProductPropertyValueRespDTO skuProperty : skuProperties) {
                for (ProductPropertyDO propertyDO : propertyExists) {
                    if (Objects.equals(propertyDO.getName(), skuProperty.getPropertyName())) {
                        skuProperty.setPropertyId(propertyDO.getId());
                        break;
                    }
                }
                for (ProductPropertyValueDO propertyValueDO : propertyValueExists) {
                    if (Objects.equals(propertyValueDO.getName(), skuProperty.getValueName())) {
                        skuProperty.setValueId(propertyValueDO.getId());
                        break;
                    }
                }
            }
            updateObj.setProperties(skuProperties);
            updateObjs.add(updateObj);
        }
        log.debug("[syncProperty][skuDOS({})个赋值 updateObjs({})]", skuDOS.size(), updateObjs.stream().map(ProductSkuDO::getProperties).collect(Collectors.toList()));
        productSkuMapper.updateById(updateObjs);
    }

}
